`timescale 1ns / 1ps
`include "defines.v"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/08 16:43:44
// Design Name: 
// Module Name: id
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 对指令进行译码，得到运算类型、子类型、源操作数1、源操作数2、目的寄存器
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module id(
    input wire rst,
    input wire[`InstAddrBus] pc_i,          // 译码阶段指令地址
    input wire[`InstBus] inst_i,            // 译码阶段指令

    // 读取Regfile的值
    input wire[`RegBus] reg1_data_i,        // 第一个读寄存器端口的输入
    input wire[`RegBus] reg2_data_i,        // 第二个读寄存器端口的输入

    // 输出到Regfile的信息
    output reg reg1_read_o,                 // 第一个读寄存器端口的使能
    output reg reg2_read_o,                 // 第二个读寄存器端口的使能
    output reg[`RegAddrBus] reg1_addr_o,    // 第一个读寄存器的读地址信号
    output reg[`RegAddrBus] reg2_addr_o,    // 第二个读寄存器的读地址信号

    // 送入到执行阶段的信息
    output reg[`AluOpBus] aluop_o,          // 要进行运算的子类型
    output reg[`AluSelBus] alusel_o,        // 要进行运算的类型
    output reg[`RegBus] reg1_o,             // 源操作数1
    output reg[`RegBus] reg2_o,             // 源操作数2
    output reg[`RegAddrBus] wd_o,           // 目的寄存器地址
    output reg wreg_o                       // 是否有要写的目的寄存器
    );

    // 取得指令的指令码、功能码
    wire[5:0] op = inst_i[31:26];
    wire[4:0] op2 = inst_i[10:6];
    wire[5:0] op3 = inst_i[5:0];
    wire[4:0] op4 = inst_i[20:16];

    // 保存指令执行需要的立即数
    reg[`RegBus] imm;

    // 指示指令是否有效
    reg instvalid;

    /**
     ** 第一阶段：对指令进行译码，首先进行基本的初始化
    */ 
    always @ (*) begin
        if (rst == `RstEnable) begin
            aluop_o <= `EXE_NOP_OP;     // 子指令类型也是空
            alusel_o <= `EXE_RES_NOP;   // 一级指令为空
            wd_o <= `NOPRegAddr;        // 寄存器为0
            wreg_o <= `WriteDisable;    // 不写目的寄存器
            instvalid <= `InstValid;    // 指令无效
            reg1_read_o <= 1'b0;        // 第一个读端口使能关闭
            reg2_read_o <= 1'b0;        // 第二个读端口使能关闭
            reg1_addr_o <= `NOPRegAddr; // 空地址
            reg2_addr_o <= `NOPRegAddr; // 空地址
            imm <= 32'h0;               // 将立即数设置为0
        end else begin
            aluop_o <= `EXE_OR_OP;      // 子指令类型为空
            alusel_o <= `EXE_RES_NOP;   // 以及指令为空
            wd_o <= inst_i[15:11];      // 目的寄存器地址
            wreg_o <= `WriteDisable;    // 禁止写
            instvalid <= `InstInvalid;  // 指令无效
            reg1_read_o <= 1'b0;
            reg2_read_o <= 1'b0;
            reg1_addr_o <= inst_i[25:21];   // 读端口1的寄存器地址
            reg2_addr_o <= inst_i[20:16];   // 读端口2的寄存器地址
            imm <= `ZeroWord;           // 立即数为0

            case (op)
                `EXE_ORI: begin                     // 依据op的值判断是否是ori指令
                    wreg_o <= `WriteEnable;         // ori指令需要写入目的寄存器
                    aluop_o <= `EXE_OR_OP;          // 子类型是逻辑或运算
                    alusel_o <= `EXE_RES_LOGIC;     // 运算类型是逻辑运算
                    reg1_read_o <= 1'b1;            // regfile的读端口1读取寄存器
                    reg2_read_o <= 1'b0;            // 不需要寄存器读端口2
                    imm <= {16'h0, inst_i[15:0]};   // 指令扩展
                    wd_o <= inst_i[20:16];          // 目的地址寄存器
                    instvalid <= `InstValid;        // 指令有效 
                end
                default: begin
                end
            endcase
        end
    end

    /**
    **  第二阶段：确定进行运算的源操作数1
    **/
    always @ (*) begin
        if (rst == `RstEnable) begin
            reg1_o <= `ZeroWord;            // 如果是复位，则返回0
        end else if (reg1_read_o == 1'b1) begin
            reg1_o <= reg1_data_i;          // 读端口1的输出值
        end else if (reg1_read_o == 1'b0) begin
            reg1_o <= imm;                  // 立即数
        end else begin
            reg1_o <= `ZeroWord;            
        end
    end

    /**
    ** 第三阶段：确定进行运算的源操作数2
    **/
    always @ (*) begin
        if (rst == `RstEnable) begin
            reg2_o <= `ZeroWord;
        end else if (reg2_read_o == 1'b1) begin
            reg2_o <= reg2_data_i;
        end else if (reg2_read_o == 1'b0) begin
            reg2_o <= imm;
        end else begin
            reg2_o <= `ZeroWord;
        end
    end

endmodule
